home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / ada / gwuada_4.zip / SCRIPT.C < prev    next >
C/C++ Source or Header  |  1990-07-25  |  9KB  |  370 lines

  1. /*
  2.  * Script for MSDOS
  3.  * Version 1.1
  4.  * Written Nov 1987 by graham@sce.carleton.ca (Doug Graham)
  5.  *
  6.  * This program is similar to the UNIX command of the same name.
  7.  * When running it, output that appears on the console, will also
  8.  * be saved into a file for later perusal.
  9.  *
  10.  * usage is: "script [-f outputfile] [-a] [command]"
  11.  *
  12.  * default outputfile is "typescript" in the current directory.
  13.  *
  14.  * -a means to append to outfile.
  15.  *
  16.  * Script optionally takes a command as argument. If one is given,
  17.  * this command is executed rather than "command.com".
  18.  * This saves having an extra copy of command.com wasting space
  19.  * in memory.
  20.  *
  21.  * BUGS:
  22.  *
  23.  * 1)    On output, script first writes all the data to the output
  24.  *    file, and then chains to DOS so that DOS does the actual console
  25.  *    output. If a ^C is hit while this console output is taking place,
  26.  *    console output is stopped. However this data has already been
  27.  *    written to the output file. The result is that more data can appear
  28.  *    in the output file than actually appeared on the screen.
  29.  *
  30.  * 2)    On input, script calls DOS using it's own stack and with
  31.  *    the flag "onintstack" set. (See int21.asm) If a ^C is typed
  32.  *    in response to the input request, the calling program is
  33.  *    aborted leaving "onintstack" set. This causes script to stop
  34.  *    saving output to the output file.
  35.  */
  36.  
  37. /*
  38.  * Modification History:
  39.  *   Sept 2/89 Doug Graham.
  40.  *    Modified to work with Turbo C 2.0 compiler.
  41.  *    Also fiddled it a bit to reduce size.    
  42.  */
  43.  
  44. #include <stdio.h>
  45. #include <stdlib.h>
  46. #include <fcntl.h>
  47. #include <sys/stat.h>
  48. #include <process.h>
  49. #include <dos.h>
  50. #include <time.h>
  51.  
  52. static int    fd;
  53. static int    mypsp;
  54. static int    criterr_occurred;
  55. static char far *dosflag;
  56. extern char far *getdosflag();
  57.  
  58. /*
  59.  * It might be that heaplen should be increased if
  60.  * there is a lot of stuff in the environment, because the TC startup
  61.  * routines mallocate space for both the environment, and the command
  62.  * line arguments.
  63.  */
  64. unsigned    _heaplen = 1000;
  65. unsigned    _stklen = 1000;
  66.  
  67. main(argc, argv)
  68. char **argv;
  69. {
  70.     int    oflags = O_WRONLY|O_CREAT|O_TRUNC|O_TEXT;
  71.     char    *ofile = "typescript";
  72.     char    *command;
  73.     char    *GetTime();
  74.  
  75.     for (--argc,++argv; argc && (argv[0][0] == '-'); --argc,++argv) {
  76.         switch(argv[0][1]) {
  77.         case 'f':
  78.             if (! --argc)
  79.                 usage();
  80.             ofile = *++argv;
  81.             break;
  82.         case 'a':
  83.             oflags = (oflags & ~O_TRUNC) | O_APPEND;
  84.             break;
  85.         default:
  86.             usage();
  87.         }
  88.     }
  89.  
  90.     command = argc ? *argv : getenv("COMSPEC");
  91.     if (! command)
  92.         command = "command";
  93.  
  94.     if ((fd = open(ofile, oflags, S_IREAD|S_IWRITE)) < 0) {
  95.         mprintf("Can't open %s for writing\r\n", ofile);
  96.         exit(1);
  97.     }
  98.  
  99.     dosflag = getdosflag();
  100.     mypsp = getpsp();
  101.  
  102.     if (! grab21()) {
  103.         mprintf("I think script is already active\r\n");
  104.         exit(1);
  105.     }
  106.  
  107.     mprintf("Script V1.1 session started %s\r\n", GetTime());
  108.  
  109.     if (spawnvp(P_WAIT, command, argv) == -1)
  110.         mprintf("Can't execute %s\r\n", command);
  111.  
  112.     mprintf("Script completed %s\r\n", GetTime());
  113.  
  114.     rstr21();
  115.     flushbuf();
  116.     close(fd);
  117.  
  118.     mprintf("Output file is %s\r\n", ofile);
  119.     exit(0);
  120. }
  121.  
  122. usage()
  123. {
  124.     mprintf("usage: script [-f outputfile] [-a] [command]\r\n");
  125.     exit(1);
  126. }
  127.  
  128. #define BUFFERSIZE    4096
  129.  
  130. static char        buffer[BUFFERSIZE];
  131. static int        bufslots = BUFFERSIZE;
  132. static char        *bufp = buffer;
  133.  
  134. #define _putc(c) \
  135.     {*bufp++ = c; if (! --bufslots) flushbuf();}
  136.  
  137. /*
  138.  * When flushing the buffer to disk, we use the undocumented DOS function
  139.  * 50h (set PSP) so that script's file handles are used rather
  140.  * than the the calling program's. I'm not sure in which versions
  141.  * of DOS this function exists.
  142.  *
  143.  * Control break checking is turned off in case the user types a
  144.  * ^C just as we are about to do the write to disk. Since we switched
  145.  * PSP's, DOS thinks we are the active program, and if ^C is typed
  146.  * with break checking enabled, it is us that will be aborted rather
  147.  * than the program running under us. This causes major havoc.
  148.  * The DOS critical error vector is intercepted for the same reason.
  149.  * Our handler simply sets a flag if an error occurs; this flag is
  150.  * checked at a later time.
  151.  */
  152. struct tcframe {int bp, di, si, ds, es, dx, cx, bx; unsigned char al, ah;};
  153.  
  154. void    interrupt
  155. my_criterr_handler(regs)
  156. struct tcframe regs;
  157. {
  158.     criterr_occurred = 1;
  159.     regs.al = 0;    /* Zero means ignore the error. */
  160. }
  161.  
  162. #define CRITERR_VECT    0x24
  163.  
  164. flushbuf()
  165. {
  166.     int hispsp;
  167.     int hiscbrk;
  168.     void interrupt (* old_criterr_handler)();
  169.  
  170.     hiscbrk = getcbrk();
  171.     setcbrk(0);
  172.     old_criterr_handler = getvect(CRITERR_VECT);
  173.     setvect(CRITERR_VECT, my_criterr_handler);
  174.     hispsp = getpsp();
  175.     setpsp(mypsp);
  176.     criterr_occurred = 0;
  177.  
  178.     _write(fd, buffer, bufp - buffer);
  179.     bufslots = BUFFERSIZE;
  180.     bufp = buffer;
  181.  
  182.     setpsp(hispsp);
  183.     setvect(CRITERR_VECT, old_criterr_handler);
  184.     setcbrk(hiscbrk);
  185.     if (criterr_occurred)
  186.         mprintf("\r\n\r\nSCRIPT: WARNING: disk write failed\r\n\r\n");
  187. }
  188.  
  189. #define isconsole(handle) ((ioctl(handle, 0) & 0x82) == 0x82)
  190.  
  191. union    MYFRAME    {
  192.     struct {unsigned int    ax, bx, cx, dx, ds, es;} x;
  193.     struct {unsigned char    al, ah, bl, bh, cl, ch, dl, dh;} h;
  194. };
  195.  
  196. /*
  197.  * DOS output functions. There are probably more, but these seem
  198.  * to do the job for me.
  199.  */
  200. #define CHAR_OUT    0x02
  201. #define DIRECT_OUT    0x06
  202. #define STRING_OUT    0x09
  203. #define WRITE_FILE    0x40
  204.  
  205. /*
  206.  * DOS input functions. The input functions which also echo the
  207.  * input to the console must be intercepted, because otherwise
  208.  * this echo output would not be saved in the script file. There
  209.  * are probably more of these type of functions, but my documentation
  210.  * is not clear on which functions echo, and which do not, and I
  211.  * don't have the patience to go through and try each one.
  212.  */
  213. #define CHAR_IN_ECHO    0x01
  214. #define READ_FILE    0x3F
  215. #define BUFFERED_INPUT    0x0A
  216.  
  217. /*
  218.  * Returning the ZERO_FLAG to the first level handler tells
  219.  * it to chain to the old int 21 handler. If this bit is
  220.  * not set in the returned value, no such chaining occurs.
  221.  */
  222. #define ZERO_FLAG    0x40
  223. #define CARRY_FLAG    0x01
  224.  
  225. /*
  226.  * Called by the assembly language first level handler. The first level handler
  227.  * first switches stacks, builds a stack frame that looks "union MYFRAME"
  228.  * and then calls "int21handler".
  229.  */
  230. unsigned
  231. int21handler(regs)
  232. union MYFRAME regs;
  233. {
  234.     unsigned char far *fp;
  235.     int c, len, flags;
  236.  
  237.     /*
  238.      * A bit of paranoia below. Since this function is only called
  239.      * when a program is trying to call DOS, it could possibly be
  240.      * safely assumed that it is not already in DOS. Just to be
  241.      * sure, I check the undocumented "indos" flag. It is important
  242.      * that nobody is in DOS when this procedure executes because
  243.      * it calls DOS itself, and DOS is not re-entrant.
  244.      */
  245.     if (*dosflag)
  246.         return (ZERO_FLAG);
  247.  
  248.     switch (regs.h.ah) {
  249.     case CHAR_OUT:
  250.         if (isconsole(1))
  251.             _putc(regs.h.dl);
  252.         break;
  253.     case DIRECT_OUT:    /* This ones a real crock!! */
  254.         if ((isconsole(1)) && (regs.h.dl != 0xFF))
  255.             _putc(regs.h.dl);
  256.         break;
  257.     case STRING_OUT:
  258.         if (isconsole(1)) {
  259.             fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
  260.             while ((c = *fp++) != '$')
  261.                 _putc(c);
  262.         }
  263.         break;
  264.     case WRITE_FILE:
  265.         if (isconsole(regs.x.bx)) {
  266.             fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
  267.             for (len = regs.x.cx; len--; )
  268.                 _putc(*fp++);
  269.         }
  270.         break;
  271.     case READ_FILE:
  272.         if (isconsole(regs.x.bx)) {
  273.             fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
  274.             flags = callDOS(®s);
  275.             if (! (flags & CARRY_FLAG))
  276.                 for (len = regs.x.ax; len--; )
  277.                     _putc(*fp++);
  278.             return (flags & ~ZERO_FLAG);
  279.         }
  280.         break;
  281.     case CHAR_IN_ECHO:
  282.         if (isconsole(0)) {
  283.             flags = callDOS(®s);
  284.             _putc(regs.h.al);
  285.             return (flags & ~ZERO_FLAG);
  286.         }
  287.         break;
  288.     case BUFFERED_INPUT:
  289.         if (isconsole(0)) {
  290.             flags = callDOS(®s);
  291.             fp = (unsigned char far *)MK_FP(regs.x.ds, regs.x.dx);
  292.             for (len = *++fp; len--; )
  293.                 _putc(*++fp);
  294.             return (flags & ~ZERO_FLAG);
  295.         }
  296.         break;
  297.     }
  298.     return (ZERO_FLAG);
  299. }
  300.  
  301. /*
  302.  * Use my own printf in order to save a couple of Kbytes in the executable.
  303.  * The re